﻿// Sequin.Sequences.IO.SequenceReader
//
// (c) 2011 Arthur Pitman
//
// Part of the Sequin Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License (Version 2.1) as published by the Free Software Foundation.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License (Version 2.1) along with this library; if not, write to the 
// Free Software Foundation, Inc., 
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
// or see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;

namespace Sequin.Sequences.IO
{
	/// <summary>
	/// Reads sequences from a file
	/// </summary>
	public class SequenceReader : ISequenceReader
	{
		/// <summary>
		/// The <see cref="StreamReader"/> associated with the <see cref="SequenceReader"/>
		/// </summary>
		protected StreamReader reader = null;

		/// <summary>
		/// The <see cref="ISymbolMapper"/> associated with the <see cref="SequenceReader"/> 
		/// </summary>
		protected ISymbolMapper symbolMapper = null;

		/// <summary>
		/// The <see cref="ISequenceFormatter"/> associated with the <see cref="SequenceReader"/> 
		/// </summary>
		ISequenceFormatter sequenceFormatter;

		/// <summary>
		/// Reads a series of <see cref="Sequence"/> objects from a file 
		/// </summary>
		/// <param name="filename">The filename to open and read from</param>
		/// <param name="symbolMapper">The <see cref="ISymbolMapper"/> to use</param>
		/// <param name="compressed">Indicates if the file is compressed</param>		
		/// <returns>A <see cref="List{Sequence}"/></returns>
		public static List<Sequence> ReadSequencesFromFile(string filename, ISymbolMapper symbolMapper, bool compressed = false)
		{			
			StreamReader streamReader = null;
			if (compressed)
				streamReader = new StreamReader(new GZipStream(new FileStream(filename, FileMode.Open), CompressionMode.Decompress));
			else
				streamReader = File.OpenText(filename);
			var sr = new SequenceReader(streamReader, symbolMapper);
			var sequenceList = new List<Sequence>();
			while (true)
			{
				var sequence = sr.ReadSequence();
				if (sequence == null)
					break;
				sequenceList.Add(sequence);
			}			
			sr.Close();
			return sequenceList;
		}

		/// <summary>
		/// Initializes a new <see cref="SequenceReader"/> instance
		/// </summary>
		/// <param name="reader">The <see cref="StreamReader"/> to read from</param>
		/// <param name="symbolMapper">The <see cref="ISymbolMapper"/> to use</param>
		public SequenceReader(StreamReader reader, ISymbolMapper symbolMapper) : this(reader, symbolMapper, new SequenceFormatter())
		{
		}

		/// <summary>
		/// Initializes a new <see cref="SequenceReader"/> instance
		/// </summary>
		/// <param name="reader">The <see cref="StreamReader"/> to read from</param>
		/// <param name="symbolMapper">The <see cref="ISymbolMapper"/> to use</param>
		/// <param name="sequenceFormatter"></param>
		public SequenceReader(StreamReader reader, ISymbolMapper symbolMapper, ISequenceFormatter sequenceFormatter)
		{
			this.reader = reader;
			this.symbolMapper = symbolMapper;
			this.sequenceFormatter = sequenceFormatter;
		}

		/// <summary>
		/// Reads a <see cref="Sequence"/>
		/// </summary>
		/// <returns>A <see cref="Sequence"/> or <c>null</c> if no more can be read</returns>
		public Sequence ReadSequence()
		{
			string line = null;
			while (line == null)
			{
				line = reader.ReadLine();
				if (line == null)
					return null;
				line = line.Trim().ToLower();
				if (line.Length == 0)
					line = null;
			}
			return sequenceFormatter.StringToSequence(line, symbolMapper);
		}

		/// <summary>
		/// Closes the <see cref="StreamReader"/> associated with this <see cref="SequenceReader"/>
		/// </summary>
		public void Close()
		{
			if (reader != null)
			{
				reader.Close();
				reader = null;
			}
		}     
	}

}
